/// ------------------------------------------------------------------------------------------------------------------------------------
///
/// MIT License
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
/// SOFTWARE.
///
/// Copyright (c) 2023 ycz. All rights reserved.
///
/// Created by ycz on  2022/1/2.
///
/// @file  y_cli.c
///
/// @brief
///     y_cli 是一种简单的串口命令行处理
///
/// ------------------------------------------------------------------------------------------------------------------------------------



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 头文件
/// ------------------------------------------------------------------------------------------------------------------------------------

#include "y_cli.h"

#include <string.h>
#include "y_sa.h"



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 全局变量
/// ------------------------------------------------------------------------------------------------------------------------------------

static CLI_CONFIG_st g_default_config = {
        .is_open     = true,  ///< 默认是否打开 命令行
        .is_fflush   = true,  ///< 默认是否 fflush 函数刷新显示
        .open_time_s = 0,     ///< 打开时长 N S 后设置状态为关闭状态
};  ///< 默认参数
static CLI_CONFIG_st g_config;   ///< 全局参数
static RINGBUF_st   *g_ringbuf;  ///< ringbuf 句柄
static TIMER_st     *g_timer;    ///< 命令行定时器 用于控制状态改变
static bool          g_status;   ///< 命令行状态



/// ------------------------------------------------------------------------------------------------------------------------------------
/// 公有函数
/// ------------------------------------------------------------------------------------------------------------------------------------

/// @brief   打印 y_cli 版本信息
void y_cli_print_version() {
    YLOG_VERSION("y_cli", Y_CLI_MAJOR, Y_CLI_MINOR, Y_CLI_PATCH);
}

/// @brief   获取命令行状态
/// @retval  true                          成功
/// @retval  false                         失败
bool y_cli_get_status() {
    return g_status;
}

/// @brief   删除待解析数据
/// @param   [in] size                     数据大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_cli_delete_data(uint16_t size) {

    if (size) {
        if (y_ringbuf_get_used_size(g_ringbuf) < size) {
            return y_ringbuf_reset(g_ringbuf);
        } else {
            return y_ringbuf_delete_data(g_ringbuf, size);
        }
    }
    return false;
}

/// @brief   解析数据
/// @param   [in] data                     数据指针
/// @param   [in] size                     数据大小
/// @retval  true                          成功
/// @retval  false                         失败
bool y_cli_parse(uint8_t *data, uint16_t size) {

    if (size) {
        if (y_ringbuf_get_free_size(g_ringbuf) >= size) {
            return y_ringbuf_write(g_ringbuf, data, size);
        } else {
            y_ringbuf_reset(g_ringbuf);
        }
    }
    return false;
}

/// @brief   打印参数
/// @param   [in] config                   参数
void y_cli_config_print(CLI_CONFIG_st *config) {
    // 断言
    YLOGA(config);
    YLOGI("CLI  is_open                      : %s", config->is_open ? "true" : "false");
    YLOGI("CLI  is_fflush                    : %s", config->is_fflush ? "true" : "false");
    YLOGI("CLI  open_time_s                  : %d", config->open_time_s);
}

/// @brief   参数获取
/// @return  参数值
CLI_CONFIG_st *y_cli_config_get() {
    return &g_config;
}

/// @brief   参数设置
/// @param   [in] config                   参数
/// @retval  true                          成功
/// @retval  false                         失败
bool y_cli_config_set(CLI_CONFIG_st *config) {
    // 断言
    YLOGA_FALSE(config);
    memcpy(&g_config, config, sizeof(g_config));  // 参数赋值
    return y_nvs_set((uint8_t *) "cli_config", (uint8_t *) &g_config, sizeof(g_config), true);
}

/// @brief   初始化
/// @retval  true                          成功
/// @retval  false                         失败
bool y_cli_init() {

    // 加载算法参数
    uint16_t size = 0;
    if (y_nvs_get((uint8_t *) "cli_config", (uint8_t *) &g_config, &size) == false || size != sizeof(g_config)) {  // 从 nvs 读取失败
        memcpy(&g_config, &g_default_config, sizeof(g_config));
        y_nvs_set((uint8_t *) "cli_config", (uint8_t *) &g_config, sizeof(g_config), true);
    }

    // 创建 ringbuf
    g_ringbuf = y_ringbuf_create(256);
    YLOGA_FALSE(g_ringbuf);

    // 创建定时器
    if (g_config.open_time_s) {
        g_timer = y_timer_create(TIMER_PERIODIC);
        YLOGA_FALSE(g_timer);
        y_timer_start(g_timer, g_config.open_time_s * 1000);  // 10s 后 无操作退出命令行状态
    }

    return true;
}

/// @brief   任务处理
/// @param   [in] time_ms                  每次调用间隔时间
void y_cli_process(uint32_t time_ms) {

    // 判断定时器是否超时
    if (y_timer_get_trigger_num(g_timer)) {
        y_timer_stop(g_timer);  // Ns 后 无操作退出命令行状态
        g_status = false;
    }

    // 判断 buf 中有无数据
    if (y_ringbuf_get_used_size(g_ringbuf) == 0) {
        return;
    }

    // 判断是否打开命令行
    if (g_config.is_open == false) {
        uint16_t buf_size = y_ringbuf_get_used_size(g_ringbuf);
        if (buf_size) {
            uint8_t buf[buf_size];
            y_ringbuf_read_clear(g_ringbuf, buf, buf_size);  // 读取数据
            if (strstr((char *) buf, "open_cli")) {
                g_config.is_open = true;
            }
        }
        return;
    }

    // 从 ringbuf 中读取数据 读取一行
    static uint16_t lsat_size = 0;
    uint16_t        index     = 0;
    bool            ret       = y_ringbuf_find_chr(g_ringbuf, '\n', &index);
    if (ret == false) {
        ret = y_ringbuf_find_chr(g_ringbuf, '\r', &index);
    }
    if (ret) {
        uint16_t buf_size = index + 1;  // 从零开始 需对齐个数
        uint8_t  buf[buf_size];
        y_ringbuf_read_clear(g_ringbuf, buf, buf_size);  // 读取数据
        buf[buf_size - 1] = 0;                           // 最后一位 修改为空
        g_status          = true;
        if (g_config.open_time_s) {
            y_timer_start(g_timer, g_config.open_time_s * 1000);  // 刷新定时器
        }
        if (g_config.cli_cb) {
            g_config.cli_cb(buf, buf_size);  // 令行数据处理函数
        }
        lsat_size = 0;
        return;
    }

    // 从 ringbuf 中查找处理 退格或删除
    ret = y_ringbuf_find_chr(g_ringbuf, '\b', &index);
    if (ret == false) {
        ret = y_ringbuf_find_chr(g_ringbuf, 127, &index);
    }
    if (ret) {
        if (index == 0) {
            y_ringbuf_delete_index(g_ringbuf, index, 1);  // 退无可退
            lsat_size = 0;
        } else {
            y_ringbuf_delete_index(g_ringbuf, index - 1, 2);  // 要删除前边的一个字符
            printf("\b \b");                                  // 回显
            if (g_config.is_fflush) {
                fflush(stdout);
            }
            lsat_size = y_ringbuf_get_used_size(g_ringbuf);
        }
        return;  // 处理命令行数据
    }

    // 打印数据
    uint16_t buf_size = y_ringbuf_get_used_size(g_ringbuf);
    uint8_t  buf[buf_size + 1];
    y_ringbuf_read_only(g_ringbuf, buf, buf_size);
    buf[buf_size] = 0;
    if (lsat_size != buf_size) {
        printf("%s", &buf[lsat_size]);
        if (g_config.is_fflush) {
            fflush(stdout);
        }
        lsat_size = buf_size;
    }
}